import os
import sys
import inspect
from xml.etree import ElementTree
from rsa.ruleset import RuleSet, RuleSets
from rsa.util.common_utilities import dynamic_import, element_text, clean

class IRuleSetLoader(object):
    
    def get_rule_sets(self, rules=None):
        """
        Returns a RuleSets object loaded from the rules parameter
        @param rules: path for the rules to load 
        """
        raise NotImplementedError("Should have implemented this")
    
class DynamicRuleSetLoader(IRuleSetLoader):
    """
    Load all rule sets in the rule set path
    """
    
    def __init__(self, rulesetpath=["rules"]):
        self.rulesets = None
        self.rulesetpath = rulesetpath
    
    def get_rule_sets(self, rules=None):
        self.rulesets = RuleSets()
        for path in self.rulesetpath:
            dirname = os.path.expanduser(path)
            if os.path.exists(dirname):
                for filename in os.listdir(dirname):
                    filename = os.path.join(dirname, filename)
                    if self.is_ruleset_file(filename):
                        ruleset = RuleSet(filename)
                        self.rulesets.add_rule_set(ruleset)
            else:
                # if it's not a directory, maybe it's the
                # name of an importable package
                try:
                    __import__(path)
                    module = sys.modules[path]
                    for child in [m  for _, m in
                                  inspect.getmembers(module, predicate=inspect.ismodule) ]:
                        ruleset = RuleSet(child.__name__)
                        self.rulesets.add_rule_set(ruleset)
                except ImportError:
                    #print "    unable to load:", e
                    pass
        return self.rulesets
                
    def is_ruleset_file(self, path):
        """ Returns true if the file looks like a rule set """
        if path.lower().endswith("ruleset.py"):
            return True
        return False
    
    def list_rulesets(self):
        """ Print the name of all of the rule sets, then it's rules. """
        print "Loaded RuleSets:"
        for ruleset in self.rulesets:
            if(len(ruleset.rules) > 0):
                print " * " + ruleset.name
                for rule in ruleset:
                    print "   - " + rule.rule_id 
    
    def describe_rules(self, rules):
        for ruleset in self.rulesets:
            rsdescribed = False
            describeall = False
            if(ruleset.name in rules):
                describeall = True
            for rule in ruleset:
                if(describeall or rule.rule_id in rules):
                    if(not rsdescribed):
                        print ruleset.name + ": " + ruleset.description.replace("\n","")
                        rsdescribed = True
                    print " - " + rule.rule_id
                    print "   " + rule.__doc__.replace("\n","")
    
class XMLRuleSetLoader(IRuleSetLoader):
    """
    XMLRuleSetLoader is responsible for creating RuleSet instances from XML content.
    """
    
    def __init__(self):
        self.unloadedRules = []

    def get_rule_sets(self, rules=None):
        """
        Create a RuleSets from a list of names.
        @param rules  A comma-separated list of rule set files.
        @throws RuleSetNotFoundException if unable to find a resource.
        """
        try:
            # Load rule set file names into a list
            file = open(rules)
            ruleSetFileNames = []
            try:
                for line in file:
                    index = line.find('=')
                    line = line[index + 1:]
                    line = clean(line)
                    sets = line.split(';')
                    ruleSetFileNames.extend(sets)
            finally:
                file.close()
            # Create and return specified rule sets 
            return self.create_rule_sets(ruleSetFileNames)
        except(IOError):
            raise RuleSetLoadingException("Couldn't find rulesets.properties; please ensure that the rulesets directory is on the classpath.")
        finally:
            if(len(self.unloadedRules) > 0):
                raise RuleSetLoadingException("Couldn't load rules: " + self.unloadedRules, ',')
            
    
    def create_rule_sets(self, ruleSetFileNames):
        """
        Returns a List of RuleSet objects loaded from the "ruleSetFileNames" resource.
        """
        ruleSets = RuleSets()
        for fileName in ruleSetFileNames:
            ruleSet = self.create_rule_set(fileName) 
            ruleSets.add_rule_set(ruleSet)
        return ruleSets
    
    def create_rule_set(self, fileName):
        """
        Parse a rule set node to construct a RuleSet.
        @param fileName an XML representation of the rule set
        """
        root = ElementTree.parse(fileName)
        if(root._root.tag == 'ruleset'):
            name = root._root.attrib.get('name')
            # create rule set 
            ruleSet = RuleSet(name)
            # add every rule to the rule set
            for children in root._root._children:
                if(children.tag == 'rule'):
                    ruleSet.add_rule(self.create_rule(children))
            return ruleSet
        return None
        
    def create_rule(self, node):
        """
        Parse a rule node as a single Rule.  The Rule has been fully defined within
        the context of the current RuleSet.
        @param node Must be a rule element node.
        """
        name = node.attrib.get('name')
        klass = node.attrib.get('class')
        message = node.attrib.get('message')
        example = element_text(node, 'example')
        priority = element_text(node, 'priority')
        parameters = element_text(node, 'parameter')
        description = element_text(node, 'description')
        try:
            instance = dynamic_import(klass)(name, message, example, description, priority)
            for param in parameters:
                instance.add_invalid_character(param)
            return instance
        except(AttributeError):
            self.unloadedRules.append(klass)
    
    
class RuleSetLoadingException(Exception):
    
    def __init__(self, message):
        self.message = message
